package com.iot08.api.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.iot08.api.config.properties.GdBdConfig;
import com.iot08.api.config.properties.ListDataProperties;
import com.iot08.api.config.redis.RedisClusterService;
import com.iot08.api.service.supplement.KafkaService;
import com.iot08.api.service.supplement.TCPService;
import com.iot08.api.utils.request.ParamHandle;
import com.iot08.api.vo.request.GetLocationTOJuHuoReqVO;
import com.iot08.api.vo.request.PosttLocationTOJuHuoReqVO;
import com.iot08.common.constant.ConstCacheField;
import com.iot08.common.constant.ConstKafka;
import com.iot08.common.entity.app.Device;
import com.iot08.common.entity.app.DeviceLocation;
import com.iot08.common.service.base.BaseService;
import com.iot08.common.utils.HttpReq;
import com.iot08.common.utils.RandomUtil;
import com.iot08.common.utils.StringUtil;
import com.iot08.common.utils.response.ResponseEnum;
import com.iot08.common.utils.response.ResponseMap;
import com.mongodb.client.FindIterable;
import com.mongodb.client.model.Filters;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @version 1.0
 * @Description:
 * @author: Rock
 * @time: 2021/1/25 15:40
 */
@Slf4j
@Service
public class DeviceLocationService extends BaseService<DeviceLocation> {

    @Resource
    private ListDataProperties listDataProperties;

    @Resource
    private DeviceService deviceService;
    @Resource
    private KafkaService kafkaService;
    @Resource
    private RedisClusterService redisClusterService;

    @Resource
    private GdBdConfig gdBdConfig;

    //是否返回判断为异常的定位点给APP进行显示
    @Value("${show.abnormal.point.status}")
    private Boolean isShowAbnormalPoint;

    /**
     * 上传某天定位点 的 解析位置数据 (文字轨迹列表)
     * @param imei
     * @param vo
     * @time: 4/29/22 10:22 AM
     * @author: Weichao
     * @version 1.0
     */
    public ResponseMap postLocationToJuHuo(String imei, PosttLocationTOJuHuoReqVO vo){
        log.info("imei:{} 调用更新定位轨迹点", imei);
        try {
            JSONArray locationsObject = JSONArray.parseArray(vo.getLocations());
            for (int i= 0; i < locationsObject.size(); i++) {
                JSONObject item = locationsObject.getJSONObject(i);
                String locationId = item.getString("locationId");
                String address = item.getString("address");
                Update update = new Update();
                update.set("addr", address);
                update.set("appGeocoding", 1);
                if(locationId.length() == 32){
                    this.updateById(locationId, update);
                }else{
                    this.updateById(new ObjectId(locationId), update);
                }

                log.info("imei: {} 更新定位轨迹的 地址 : {}  id: {} ", imei, address, locationId);
            }
        }catch (Exception error){
            log.error("imei:"+imei+" 调用更新定位轨迹点失败", error);
            return ResponseMap.fail("调用更新定位轨迹点失败");
        }
        return ResponseMap.success();
    }
    /**
     * 查询设备定位数据 (历史轨迹)
     * @param imei
     * @param vo
     * @time: 2021/2/4 9:22
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap getLocationToJuHuo(String imei, GetLocationTOJuHuoReqVO vo) {
        ResponseMap res = ResponseMap.success();
        List<Object> locationList = new ArrayList<>();
        List<Object> isAbnormalList = new ArrayList<>();
        Document query = new Document();
        query.put("i", imei);
        //参数query处理
        ParamHandle.queryParamHandle(query, vo.getQuery());
        FindIterable<Document> document = this.findDoc(query, Filters.eq("t", -1), vo.getPage(), vo.getNum());
        for (Document doc : document) {
            Map<String, Object> temp = new HashMap<>();
            Double lon = ObjectUtils.isNotEmpty(doc.get("lon"))?Double.parseDouble(doc.getString("lon")):0;
            Double lat = ObjectUtils.isNotEmpty(doc.get("lat"))?Double.parseDouble(doc.getString("lat")):0;
            //过滤掉定位有问题的数据
            if(!((Double.valueOf(0).equals(lon) && Double.valueOf(0).equals(lat)) || ((Double.valueOf(-1).equals(lon) && Double.valueOf(-1).equals(lat))))){
                temp.put("id", doc.get("_id").toString());
                temp.put("lon", doc.get("lon"));
                temp.put("lat", doc.get("lat"));
                temp.put("useBgAdd", doc.get("useBgAdd") == null ? 0 : doc.get("useBgAdd"));
                temp.put("loc_type", doc.get("T") == null ? "LBS" : doc.get("T"));
                temp.put("address", doc.get("addr") == null ? "" : doc.get("addr"));
                if(temp.get("loc_type").toString().equalsIgnoreCase("GPS") && temp.get("address").toString().isEmpty()){
                    //GPS 点且没有详细地址
                    JSONObject gpsRes = TCPService.toGPSparsing(listDataProperties, new JSONObject(temp), imei, 460,gdBdConfig.isBdOrGd);
                    Update update = new Update();
                    update.set("addr", gpsRes.getString("formatted_address"));
                    temp.put("address", gpsRes.getString("formatted_address"));
                    JSONObject addressComponent = gpsRes.getJSONObject("addressComponent");
                    if (addressComponent != null) {
                        update.set("adcode", addressComponent.getString("adcode"));
                        update.set("city", addressComponent.getString("city"));
                    }
                    this.updateById(doc.get("_id"), update);
                }
                temp.put("timestamp", doc.get("t"));
                if (isShowAbnormalPoint) {
                    if (StringUtil.isNotBlank(doc.get("isAbnormal")) && "1".equals(doc.get("isAbnormal").toString())) {
                        isAbnormalList.add(temp);
                        continue;
                    }
                }
                locationList.add(temp);
            }
        }
        if (isAbnormalList.size() > 0) {
            log.info("imei:{} 查询设备定位数据其中存在纠偏过滤后标记的异常点: {}", imei, isAbnormalList);
        }
        res.put("locations", locationList);
        return res;
    }

    /**
     * 查询设备最后一次定位
     *
     * @param imei
     * @time: 2021/2/4 9:47
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap getLocationLastToJuHuo(String imei, Integer qAddress) {
        Device device = deviceService.findById(imei);
        if (device == null) {
            log.error("Device is null. id={}. getLocationLastToJuHuo", imei);
            return ResponseMap.fail(ResponseEnum.STATUS2001.getCode(), ResponseEnum.STATUS2001.getMsg(), imei);
        }
        ResponseMap res = ResponseMap.success();
        if ((device.getLoc() != null && device.getLoc().size() > 0) || (device.getNowloc() != null && device.getNowloc().size() > 0)) {
            Map<String, Object> temp = new HashMap<>();
            if (device.getNowloc() == null || device.getNowloc().size() <= 0) {
                temp.put("id", device.getLoc().get("_id"));
                temp.put("lon", device.getLoc().get("lon"));
                temp.put("lat", device.getLoc().get("lat"));
                temp.put("useBgAdd", device.getLoc().get("useBgAdd") == null ? 0 : device.getLoc().get("useBgAdd"));
                temp.put("loc_type", device.getLoc().get("T") == null ? "LBS" : device.getLoc().get("T"));
                temp.put("address", device.getLoc().get("addr") == null ? "" : device.getLoc().get("addr"));
                temp.put("radius", device.getLoc().get("radius"));
                temp.put("timestamp", device.getLoc().get("t"));
            } else {
                if (device.getLoc() != null && device.getLoc().size() > 0) {
                    if (StringUtil.isNumeric(device.getLoc().get("t"))
                            && StringUtil.isNumeric(device.getNowloc().get("timestamp"))
                            && Double.parseDouble(device.getNowloc().get("timestamp").toString()) >= Double.parseDouble(device.getLoc().get("t").toString())) {
                        temp.put("id", device.getNowloc().get("imei"));
                        temp.put("lon", device.getNowloc().get("lon"));
                        temp.put("lat", device.getNowloc().get("lat"));
                        temp.put("useBgAdd", device.getNowloc().get("useBgAdd") == null ? 0 : device.getNowloc().get("useBgAdd"));
                        temp.put("loc_type", device.getNowloc().get("loctype") == null ? "LBS" : device.getNowloc().get("loctype"));
                        temp.put("address", device.getNowloc().get("address") == null ? "" : device.getNowloc().get("address"));
                        temp.put("timestamp", device.getNowloc().get("timestamp"));
                    } else {
                        temp.put("id", device.getLoc().get("_id"));
                        temp.put("lon", device.getLoc().get("lon"));
                        temp.put("lat", device.getLoc().get("lat"));
                        temp.put("useBgAdd", device.getLoc().get("useBgAdd") == null ? 0 : device.getLoc().get("useBgAdd"));
                        temp.put("loc_type", device.getLoc().get("T") == null ? "LBS" : device.getLoc().get("T"));
                        temp.put("address", device.getLoc().get("addr") == null ? "" : device.getLoc().get("addr"));
                        temp.put("timestamp", device.getLoc().get("t"));
                    }
                    temp.put("radius", device.getLoc().get("radius"));
                }
            }
            if(!temp.isEmpty()){
                if (qAddress == 1) {
                    //检测地址是否存在
                    try{
                        Object address = temp.get("address");
                        if(address == null || StringUtils.isEmpty((String)address)){
                            if(gdBdConfig.getIsBdOrGd() == 1){
                                //高德逆地址解析
                                gdInverseAddressResolution(imei, temp, device);
                            }else{
                                //百度逆地址解析
                                bdInverseAddressResolution(imei, temp, device);
                            }
                        }
                    }catch (Exception e){
                        log.error("查询设备最后一次定位时请求 qAddress==1 请求地址位置时失败:"+e.getMessage(), e);
                    }
                }
                res.put("location", temp);
            }
        }
        return res;
    }

    /**
     * @description 高德逆地址解析
     * @author cwj
     * @param [imei, temp, device]
     * @return void
     * @date @time 2024/10/16 15:56
     **/
    private void gdInverseAddressResolution(String imei, Map<String, Object> temp, Device device) {
        //调用逆地理位置编码
        Map<String, Object> param = new HashMap<>();
        param.put("key", listDataProperties.getAmapWebKey().get(RandomUtil.getRandomInt(0, listDataProperties.getAmapWebKey().size())));
        param.put("location", temp.get("lon") + "," + temp.get("lat"));
        String convertURL = "http://restapi.amap.com/v3/geocode/regeo?" + HttpReq.paramJoint(param);
        String response = HttpReq.sendGet(convertURL);
        log.info("进行了逆地理位置编码 url = {}. response = {}", convertURL, response);
        if (StringUtil.isNotBlank(response)) {
            JSONObject regeocode = JSONObject.parseObject(response).getJSONObject("regeocode");
            String aAddress = regeocode.getString("formatted_address");
            if(StringUtil.isNotBlank(aAddress)){
                //存起来
                if(temp.get("id").toString().equalsIgnoreCase(imei)){
                    // 是当前点
                    device.getNowloc().put("address", aAddress);
                    JSONObject addressComponent = regeocode.getJSONObject("addressComponent");
                    if (addressComponent != null) {
                        device.getNowloc().put("adcode", addressComponent.getString("adcode"));
                        device.getNowloc().put("city", addressComponent.getString("city"));
                    }
                }else{
                    // 是轨迹点
                    device.getLoc().put("address", aAddress);
                    this.updateById(temp.get("id").toString(), new Update().set("addr", aAddress));
                }

                deviceService.save(device);
                temp.put("address", aAddress);    
            }
            
        }
    }

    /**
     * @description 百度逆地址解析
     * @author cwj
     * @param [imei, temp, device]
     * @return void
     * @date @time 2024/10/16 15:58
     **/
    private void bdInverseAddressResolution(String imei, Map<String, Object> temp, Device device) {
        //调用逆地理位置编码
        Map<String, Object> param = new HashMap<>();
        param.put("ak", listDataProperties.getAmapBdWebLBSKey().get(RandomUtil.getRandomInt(0, listDataProperties.getAmapBdWebLBSKey().size())));
        param.put("location", temp.get("lat")+ ","+temp.get("lon"));
        param.put("output", "json");
        param.put("coordtype", "gcj02ll");
        param.put("extensions_poi", "0");
        String convertURL = "https://api.map.baidu.com/reverse_geocoding/v3/?";
        try{
            JSONObject responseJson = HttpReq.sendBdGet(convertURL,param);
            log.info("进行了逆地理位置编码 url = {}. response = {}", convertURL, responseJson);
            if (ObjectUtils.isNotEmpty(responseJson)) {
                JSONObject regeocode = responseJson.getJSONObject("result");
                String aAddress = regeocode.getString("formatted_address");
                if(StringUtil.isNotBlank(aAddress)){
                    //存起来
                    if(temp.get("id").toString().equalsIgnoreCase(imei)){
                        // 是当前点
                        device.getNowloc().put("address", aAddress);
                        JSONObject addressComponent = regeocode.getJSONObject("addressComponent");
                        if (addressComponent != null) {
                            device.getNowloc().put("adcode", addressComponent.getString("adcode"));
                            device.getNowloc().put("city", addressComponent.getString("city"));
                        }
                    }else{
                        // 是轨迹点
                        device.getLoc().put("address", aAddress);
                        this.updateById(temp.get("id").toString(), new Update().set("addr", aAddress));
                    }
                    deviceService.save(device);
                    temp.put("address", aAddress);
                }
            }
        }catch (Exception e){
            log.error("调用百度逆地址解析接口出现异常，异常信息为：{}",e.getMessage());
        }
    }

    /**
     * 请求设备当前定位
     *
     * @param imei
     * @param openid
     * @time: 2021/2/4 9:53
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap getNowLocationToJuHuo(String imei, String openid) {
        Device device = deviceService.findById(imei);
        if (device == null) {
            log.error("Device is null. id={}. getNowLocationToJuHuo openid={}", imei, openid);
            return ResponseMap.fail(ResponseEnum.STATUS2001.getCode(), ResponseEnum.STATUS2001.getMsg(), imei);
        }
        String onlineStatus = redisClusterService.hget(imei, ConstCacheField.ONLINE_STATUS);
        if (!"1".equals(onlineStatus)) {
            return ResponseMap.fail(ResponseEnum.STATUS5001.getCode(), ResponseEnum.STATUS5001.getMsg());
        }
        kafkaService.emitDeviceDemand(imei, ConstKafka.DEVICE_TYPE_RequestLocation, null, openid);
        return ResponseMap.success();
    }

}
